/* 
 * File:   Aritmetico.h
 * Author: marce,nico
 *
 * Created on 8 de junio de 2012
 */

typedef struct {
	char 				caracter;
	uInt 				frecuencia;
}CharFrec;

typedef struct {
	uChar 				techo;
	uChar 				piso;
	vector<CharFrec>	tblFrecuencias;
}Intervalo;

class Aritmetico {
	
	public:
		void setInputFilename(const char*);
		void setOutputFilename(const char*);
		
		bool debug;
	
	protected:
		char* inputFilename;
		char* outputFilename;
};

class AritmeticoCompress : public Aritmetico{

	private:
		bool imAtFirstBlock;
		
	protected:
		Intervalo intervalo;
		
		uChar contador_underflow;
		uChar contador_last_underflow;
		
		// Ultimo piso, debe guardase para cuando se termina la compresion
		// es el piso que hay antes de verificar Overflow
		uChar lastPiso;
		
		// Suma de toda las frecuencias de todos los caracteres que estan
		// dentro de la tabla de frecuencias
		uInt  contador_frecuencias;
		
		// Mantiene la cuenta de cuantos bits se agregaron en buffer_byte_a_emitir
		// Cuando llega a ocho se reinicia a cero.
		char contador_bits_emitidos;
		
		// Buffer para ir guardando bit a bit la salida. Cuando se completa a byte
		// se guada en el string de salida.
		char buffer_byte_a_emitir;
		
		// Tamanio del bloque en Bytes
		uInt blockSize;
		
		ifstream inputFile;
		ofstream* outputFile;
		
		bool readingFromFile;
		
		// Indica cuanto se leyo en la ultima lectura del archivo
		uInt lastReadSize;
		
		string salidaComprimida;
		
	public:
		AritmeticoCompress(bool useAllASCII);
		AritmeticoCompress(const char* inputFilename , const uInt blockSize , bool useAllASCII);
		~AritmeticoCompress();
		
		void setTablaChars( const vector<char> );
		
		// Comprime un bloque del archivo de entrada y
		// guarda el resultado en el archivo de output
		int runNextBlock( string* salidaComprimida );
		
		// Comprime un texto pasado por parametro
		int runBlock(const char* texto , string* salidaComprimida , bool isEOF , bool completarAbyte );
		
		// Comprime un caracter
		int compressChar(const char , bool is_last_char );
		
		// Comprime todo el archivo
		int run();
		
		// Da la salida comprimida del archivo (del bloque)
		int getSalidaComprimida(string* salidaComprimida , bool completarAbyte);
		
		// DEBUG
		void mostrarTabla();
		
		int actualizarTblFrecuencias(char);
		
		// Calcula el techo de un caracter, sumando todas las frecuencias
		// y de los demas caracteres hasta llegar a el, dividido por la suma
		// de todas las frecuencias.
		float calcularTecho(char);
		
		float calcularPiso(char);
		
	private:
		
		int normalizarPorEnteros();
		
		int recalcularNuevoPisoYTecho();
		
		int resolverOverflow();
		
		bool hayUnderflow(char techo,char piso);
		int resolverUnderflow();
		
		int resolverUltimoChar(char);
		
		int getNextBlockOfText(char** textoLeido);
		
		// Guarda los bits en un buffer hasta completar a byte y lo guarda en string
		void emitirBit(bool);
		
		//void corrimientoDelPisoYTecho(unsigned short cantidadDeDesplazamiento);
};


class AritmeticoDecompress : public Aritmetico {
	
	private:
		bool imAtFirstBlock;
		
	protected:
		Intervalo intervalo;
		
		uChar contador_overflow;
		uChar contador_underflow;
		
		string* tira_de_bytes;
		
		// Suma de toda las frecuencias de todos los caracteres que estan
		// dentro de la tabla de frecuencias
		uInt  contador_frecuencias;
		
		// Mantiene la cuenta de cuantos bits se corrieron en buffer_next_byte
		// Cuando llega a ocho se pisa con el proximo caracter a leer.
		char contador_bits_corridos;
		
		// Buffer para ir guardando el proximo byte a descomprimir. Se le van a ir
		// haciendo RSHIFTS y los bits que salen por izquierda se agregan por derecha
		// en el char actual. (Seria un double shift de assembler)
		uChar buffer_next_byte;
		
		// Posicion en la que se debe leer el proximo byte, en tira_de_bytes
		uInt buffer_next_byte_index;
		
		uChar char_actual;
		
		// Tamanio del bloque en Bytes
		uInt blockSize;
		
		ifstream inputFile;
		ofstream* outputFile;
		
		bool readingFromFile;
		
		// Indica cuanto se leyo en la ultima lectura del archivo
		uInt lastReadSize;
		
		string* salidaDescomprimida;
		
		
		
		// Simula un shift right double de assembler
		int overflow_shift();
		
		int underflow_shift();
		
		int resolverOverflow();
		
		bool hayUnderflow(uChar techo,uChar piso);
		int resolverUnderflow();
		
		int calcular_techo_y_piso();
		
		int emitirChar(char);
		
		int leer_bits(int bits);
		
	public:
	
		AritmeticoDecompress(bool useAllASCII);
		//AritmeticoDecompress(const char* inputFilename , const uInt blockSize , bool useAllASCII);
		~AritmeticoDecompress();
		
		void setTablaChars( const vector<char> );
		
		// Descomprime un bloque del archivo de entrada y
		// guarda el resultado en el archivo de output
		int runNextBlock( string* salidaDescomprimida );
		
		// Descomprime un texto pasado por parametro
		int runBlock(const char* texto , string* salidaDescomprimida );
		
		// Descomprime una tira de bytes
		int decompressString( const string* bytes , string* salidaDescomprimida );
		
		// Descomprime todo el archivo
		int run();
		
		// Da la salida comprimida del archivo (del bloque)
		int getSalidaComprimida(string* salidaComprimida , bool isEOF , bool completarAbyte);
		
		// DEBUG
		void mostrarTabla();
		
		
		

};

